package org.uweschmidt.wiimote.whiteboard.tuio;

import java.awt.GraphicsDevice;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler;
import org.uweschmidt.wiimote.whiteboard.WiimoteWhiteboard;
import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener;
import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration;
import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent;
import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener;
import org.uweschmidt.wiimote.whiteboard.ds.IRDot;
import org.uweschmidt.wiimote.whiteboard.ds.Wiimote;
import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences;
import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences.PreferencesListener;

import de.sciss.net.OSCBundle;
import de.sciss.net.OSCClient;
import de.sciss.net.OSCMessage;
import de.sciss.net.OSCServer;

/**
 * Copyright (C) 2008, Uwe Schmidt 
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE. 
 * 
 * The Software uses a third-party library (WiiRemoteJ) which is not part of
 * the Software and is subject to its own license.
 */
public class TuioTransmitter implements PreferencesListener, WiimoteDataListener, CalibrationEventListener {

	private int fseq;
	private int sseq;
	private int[] sessions;
	private Point[] last;	
	private OSCClient trans = null;
	private int port = -1;
	private String host = null;
	
	private Rectangle bounds;
	private WiimoteDataHandler dh;
	private WiimoteCalibration calibration;
	private static final WWPreferences prefs = WWPreferences.getPreferences();

	public TuioTransmitter(WiimoteDataHandler dh, WiimoteCalibration calibration) {
		this.dh = dh;
		this.calibration = calibration;
		calibration.addCalibrationEventListener(this);
		prefs.addPreferencesListener(this);
		update();
	}
	
	private void update() {
		try {
			if (prefs.isTuioEnabled()) {
				if (trans == null || prefs.getTuioPort() != port || !prefs.getTuioHost().equals(host)) {
					if (trans != null)
						trans.stop();
					
					fseq = 0;
					sseq = 0;
					sessions = new int[4];
					last = new Point[4];
					port = prefs.getTuioPort();
					host = prefs.getTuioHost();
					getScreenSize();
					
					System.out.println(host);
					trans = OSCClient.newUsing(OSCServer.UDP);
					trans.connect();
					InetSocketAddress add = new InetSocketAddress(host, port);
					trans.setTarget(add);
					System.out.println(add.toString());
					trans.start();
					dh.addWiimoteDataListener(this);
				}
			} else {
				dh.removeWiimoteDataListener(this);
				if (trans != null)
					trans.stop();
				trans = null;
			}
		} catch (IOException e) {
			e.printStackTrace();
			WiimoteWhiteboard.getLogger().log(Level.WARNING, "TUIO transmission error.", e);
		}
	}
	
	public void preferencesChanged() {
		update();
	}
	
	public void calibrationEvent(CalibrationEvent e) {
		if (e == CalibrationEvent.SCREEN_CHANGED) {
			getScreenSize();
		}
	}

	private void getScreenSize() {
		GraphicsDevice screen = calibration.getScreen();
		if (screen != null)
			bounds = screen.getDefaultConfiguration().getBounds();
	}
	
	public void irWarped(Map<Wiimote, IRDot[]> data, Point[] points) {
		if (trans == null) return;
		
		// return if all lights are off now and were previously
		if ((points[0] == null && points[1] == null && points[2] == null && points[3] == null) &&
			(last[0] == null && last[1] == null && last[2] == null && last[3] == null))
			return;
	
		// frame sequence number, optional?
		OSCBundle b = new OSCBundle();
		b.addPacket(new OSCMessage("/tuio/2Dcur", new Object[] { "fseq", fseq++ }));
		
		// alive sessions (send before cursor data)
		List<Object> alive = new ArrayList<Object>();
		alive.add("alive");
		for (int i = 0; i < 4; i++) {
			if (points[i] != null) {
				if (last[i] == null)
					sessions[i] = ++sseq;
				alive.add(sessions[i]);
			}
			last[i] = points[i];
		}
		b.addPacket(new OSCMessage("/tuio/2Dcur", alive.toArray()));

		// cursor data
		for (int i = 0; i < 4; i++) {
			Point p = points[i];
			if (p != null) {
				p.translate(-bounds.x, -bounds.y);
				b.addPacket(new OSCMessage("/tuio/2Dcur", new Object[] {
						"set",
						sessions[i],
						// position 0..1
						(float)Math.max(0, Math.min(p.x, bounds.width)) / bounds.width, (float)Math.max(0, Math.min(p.y, bounds.height)) / bounds.height,
						// dx, dy, motion
						0f, 0f, 0f
						// sizex, sizey ???
//						1f, 1f
				}));
			}
		}
		
		try {
//			OSCPacket.printTextOn(System.out, b);
			trans.send(b);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void batteryLevel(Wiimote wiimote, double level) {
	}
	
	public void irLights(Wiimote wiimote, IRDot[] lights) {
	}
	
	public void wiimoteConnected(Wiimote wiimote) {
	}
	
	public void wiimoteDisconnected(Wiimote wiimote) {
	}

}
